package com.github.manx98.okdownloader.config;

import com.github.manx98.okdownloader.exception.ReadDataFromResponseErrorException;
import com.github.manx98.okdownloader.exception.RequestFailException;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import okhttp3.Headers;
import okhttp3.Request;

import java.util.concurrent.TimeUnit;

/**
 * 用于设置下载链接的相关设置
 *
 * @author : manx98
 * @date : 2021/5/22
 */
public final class DownloadLinkConfig {


    private final Headers headers;
    private final Retryer<Short> retryer;

    private DownloadLinkConfig(Retryer<Short> retryer, Headers headers) {
        this.headers = headers;
        this.retryer = retryer;
    }

    /**
     * 创建构建器
     *
     * @return 构建器
     */
    public static Builder newBuilder() {
        return new Builder();
    }

    /**
     * 获得一个重试器
     *
     * @return 重试器
     */
    public Retryer<Short> getRetryer() {
        return retryer;
    }

    /**
     * 生成一个下载对象
     *
     * @param url   下载链接
     * @param start Range：byte={start}-{end}
     * @param end   Range：byte={start}-{end}
     * @return 下载对象
     */
    public Request getDownloadRequest(String url, long start, long end) {
        return new Request.Builder()
                .url(url)
                .headers(headers)
                .header("Range", "bytes=" + start + "-" + end)
                .build();
    }

    /**
     * 生成一个下载对象
     *
     * @param url 下载链接
     * @return 下载对象
     */
    public Request getDownloadRequest(String url) {
        return new Request.Builder()
                .headers(headers)
                .url(url)
                .build();
    }

    /**
     * 下载链接配置构建器(线程不安全)
     */
    public static class Builder {
        private final Headers.Builder headersBuilder = new Headers.Builder();
        private int maxRetries = 3;
        private int minRetryInterval = 1;
        private TimeUnit minRetryIntervalTimeUnit = TimeUnit.SECONDS;
        private int maxRetryInterval = 3;
        private TimeUnit maxRetryIntervalTimeUnit = TimeUnit.SECONDS;

        /**
         * 最小重试间隔时间(默认值 1s)
         *
         * @param minRetryInterval 最小重试间隔时间
         * @param timeUnit         时间单位
         * @return 构建器
         */
        public Builder minRetryInterval(int minRetryInterval, TimeUnit timeUnit) {
            this.minRetryInterval = minRetryInterval;
            this.minRetryIntervalTimeUnit = timeUnit;
            return this;
        }

        /**
         * 最大重试间隔时间(默认值 3s)
         *
         * @param maxRetryInterval 最大重试间隔时间
         * @param timeUnit         时间单位
         * @return 构建器
         */
        public Builder maxRetryInterval(int maxRetryInterval, TimeUnit timeUnit) {
            this.maxRetryInterval = maxRetryInterval;
            this.maxRetryIntervalTimeUnit = timeUnit;
            return this;
        }

        /**
         * 最大重试次数(默认值 3次)
         *
         * @param maxRetries 最大重试次数
         * @return 构建器
         */
        public Builder maxRetries(int maxRetries) {
            this.maxRetries = maxRetries;
            return this;
        }

        /**
         * 添加请求头(适用于cookie等多值头)
         *
         * @param key   键
         * @param value 键值
         * @return 构建器
         */
        public Builder addHeader(String key, String value) {
            this.headersBuilder.add(key, value);
            return this;
        }

        /**
         * 添加请求头(此操作会覆盖任何已经存在的值)
         *
         * @param key   键
         * @param value 键值
         * @return 构建器
         */
        public Builder setHeader(String key, String value) {
            this.headersBuilder.set(key, value);
            return this;
        }

        /**
         * 执行构建
         *
         * @return 返回downloadLink对象
         */
        public DownloadLinkConfig build() {
            final Retryer<Short> retryer = RetryerBuilder.<Short>newBuilder()
                    .retryIfException(e -> e instanceof RequestFailException || e instanceof ReadDataFromResponseErrorException)
                    .withStopStrategy(StopStrategies.stopAfterAttempt(maxRetries))
                    .withWaitStrategy(WaitStrategies.randomWait(minRetryInterval, minRetryIntervalTimeUnit, maxRetryInterval, maxRetryIntervalTimeUnit))
                    .build();
            return new DownloadLinkConfig(retryer, headersBuilder.build());
        }
    }
}
